package com.kmxd.ams.app.arrange.command;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.kmxd.ams.client.archive.IArchiveRuleCatalogSvc;
import com.kmxd.ams.client.arrange.*;
import com.kmxd.ams.core.common.constant.ArchConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.enums.CatalogTypeEnum;
import com.kmxd.ams.core.util.DynamicTableNameUtil;
import com.kmxd.ams.infra.archive.entity.ArchiveRuleCatalog;
import com.kmxd.ams.infra.arrange.base.SystemBO;
import com.kmxd.ams.infra.arrange.entity.*;
import com.kmxd.ams.infra.tmpl.dto.ArchTmplBO;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import javax.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * 刷新目录
 *
 * @author nickbi
 */
@Component
@AllArgsConstructor
public class RefreshCatalogCmd {

  private final ICatalogSvc catalogSvc;
  private final IArchiveRuleCatalogSvc archiveRuleCatalogSvc;
  private final IDocumentSvc documentSvc;
  private final IVolumeSvc volumeSvc;
  private final IProjectSvc projectSvc;
  private final IBoxSvc boxSvc;

  @Transactional(rollbackFor = Exception.class)
  public void refresh(@NotNull Catalog req) {

    // 查询门类分组设置信息
    List<ArchiveRuleCatalog> catalogRuleList =
        archiveRuleCatalogSvc.list(
            Wrappers.<ArchiveRuleCatalog>lambdaQuery()
                .eq(ArchiveRuleCatalog::getType, CatalogTypeEnum.GROUP_NODE.getCode())
                .eq(ArchiveRuleCatalog::getArchTypeId, req.getArchTypeId())
                .eq(ArchiveRuleCatalog::getTableId, req.getTableId())
                .orderByAsc(ArchiveRuleCatalog::getSort));
    if (null == catalogRuleList || catalogRuleList.isEmpty()) {
      return;
    }
    // 删除原有目录
    LambdaQueryWrapper<Catalog> wrapper =
        Wrappers.<Catalog>lambdaQuery()
            .eq(Catalog::getTableId, req.getTableId())
            .eq(Catalog::getManageType, req.getManageType());
    catalogSvc.remove(wrapper);

    // 文件级
    if (req.getTableId().contains(ArchTmplBO.DOCUMENT)) {
      QueryWrapper<Document> query = getGroupQueryWrapper(req, catalogRuleList);
      DynamicTableNameUtil.setTableName(req.getTableId());
      List<Document> list = documentSvc.list(query);
      buildCatalog(req, catalogRuleList, list);
    }
    // 案卷级
    if (req.getTableId().contains(ArchTmplBO.VOLUME)) {
      QueryWrapper<Volume> query = getGroupQueryWrapper(req, catalogRuleList);
      DynamicTableNameUtil.setTableName(req.getTableId());
      List<Volume> list = volumeSvc.list(query);
      buildCatalog(req, catalogRuleList, list);
    }

    // 盒级
    if (req.getTableId().contains(ArchTmplBO.BOX)) {
      QueryWrapper<Box> query = getGroupQueryWrapper(req, catalogRuleList);
      DynamicTableNameUtil.setTableName(req.getTableId());
      List<Box> list = boxSvc.list(query);
      buildCatalog(req, catalogRuleList, list);
    }

    // 项目级
    if (req.getTableId().contains(ArchTmplBO.PROJECT)) {
      QueryWrapper<Project> query = getGroupQueryWrapper(req, catalogRuleList);
      DynamicTableNameUtil.setTableName(req.getTableId());
      List<Project> list = projectSvc.list(query);
      buildCatalog(req, catalogRuleList, list);
    }
  }

  private <T> QueryWrapper<T> getGroupQueryWrapper(
      Catalog req, List<ArchiveRuleCatalog> catalogRuleList) {
    QueryWrapper<T> query = new QueryWrapper<>();
    query.eq(ArchConst.DEF_STATUS, req.getManageType());
    List<String> columns = Lists.newArrayList();
    catalogRuleList.forEach(
        rule -> {
            //todo...
          String field = rule.getNodeCondition();
          columns.add(field);
          query.groupBy(field);
        });
    query.select(columns);
    return query;
  }

  private void buildCatalog(
      Catalog req, List<ArchiveRuleCatalog> catalogRuleList, List<? extends SystemBO> volumeList) {
    if (ObjectUtil.isEmpty(volumeList)) {
      return;
    }
    List<Catalog> catalogList = Lists.newArrayList();
    Map<String, Catalog> map = Maps.newHashMap();
    volumeList.forEach(
        item -> {
          item.setArchTypeId(req.getArchTypeId());
          item.setTableId(req.getTableId());
          item.setStatus(req.getManageType());
          buildCatalog(item, catalogRuleList, catalogList, map);
        });
    if (ObjectUtil.isNotEmpty(catalogList)) {
      catalogSvc.saveBatch(catalogList);
    }
  }

  /**
   * 刷新目录树，只考虑新增
   *
   * @param entity
   * @param fieldList
   * @return
   */
  private void buildCatalog(
      SystemBO entity,
      List<ArchiveRuleCatalog> fieldList,
      List<Catalog> catalogList,
      Map<String, Catalog> map) {
    Map<String, Object> entityMap = BeanUtil.beanToMap(entity);
    Object status = entityMap.get(ArchConst.DEF_STATUS);
    // 只生成待整理，归档的数据目录，其他的状态直接返回
    //    if (!ImmutableSet.of(ArchStatusEnum.S0.getCode(), ArchStatusEnum.S1.getCode())
    //        .contains(Integer.parseInt(status.toString()))) {
    //      return;
    //    }
    // 记录pid，以便形成树形结构
    List<Long> pidList = Lists.newArrayList();
    pidList.add(0L);
    // 计数器
    AtomicInteger index = new AtomicInteger(0);
    // 添加信息，按分组信息复制当前数据的条件信息，用于统计数据量
    LinkedHashMap<String, Object> condition = Maps.newLinkedHashMap();

    fieldList.forEach(
        catalog -> {
          // 分组条件里面存储的是数据库中的字段类型： 下划线分割 ，如：create_by,转为实体驼峰方式
            //todo...
          String fieldCode = catalog.getNodeCondition();
          String camelFieldCode = StringUtils.underlineToCamel(fieldCode);
          Object filedValue = entityMap.get(camelFieldCode);
          // 如果当前数据为空拼接 is null 查询条件，不为空拼接对应数据条件
          Object fieldValueStr = null;
          if (filedValue instanceof String) {
            String str = Optional.ofNullable(filedValue).orElse("").toString();
            fieldValueStr = StrUtil.isBlank(str) ? Constants.QUERY_NULL_DATA_PLACEHOLDER : str;
          } else {
            fieldValueStr =
                Objects.isNull(filedValue) ? Constants.QUERY_NULL_DATA_PLACEHOLDER : filedValue;
          }

          JSONObject obj = JSONUtil.createObj();
          obj.set(camelFieldCode, fieldValueStr);
          //           拼接父级条件 dataCondition中
          condition.forEach(obj::set);
          String dataCondition = obj.toString();
          condition.put(camelFieldCode, fieldValueStr);
          String name =
              ObjectUtil.isEmpty(filedValue)
                  ? Constants.CATALOG_PLACEHOLDER
                  : filedValue.toString();
          // 查询是否存当前数据对应的目录节点
          String key =
              CharSequenceUtil.format("{},{},{}", name, fieldCode, pidList.get(index.get()));
          if (map.containsKey(key)) {
            // 节点存在
            Catalog existCatalog = map.get(key);
            pidList.add(existCatalog.getId());
          } else {
            // 节点不存在，新增节点
            // 如果节点不存在
            // 如果index不等于分组设置的字段数量，就说明存在子节点
            int currentSize = index.get() + 1;
            Boolean hasChild = currentSize == fieldList.size() ? Boolean.FALSE : Boolean.TRUE;
            long id = IdWorker.getId();
            Catalog newCatalog =
                Catalog.builder()
                    .archTypeId(entity.getArchTypeId())
                    .tableId(entity.getTableId())
                    .fondsId(entity.getFondsId())
                    .pid(pidList.get(index.get()))
                    .fieldKey(fieldCode)
                    .manageType(Integer.parseInt(status.toString()))
                    .name(name)
                    .dataCondition(dataCondition)
                    .hasChild(hasChild)
                    .sort(currentSize)
                    .type(CatalogTypeEnum.GROUP_NODE.getCode())
                    .id(id)
                    .build();
            catalogList.add(newCatalog);
            map.put(key, newCatalog);
            pidList.add(newCatalog.getId());
          }
          // 增加index
          index.getAndIncrement();
        });
  }
}
